/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

/* ---------------------------------------------------------------------------
** We'll Combine Use the :
** XmlTextReader. http://xmlsoft.org/xmlreader.html
** XPath.
** SAX : http://www.jamesh.id.au/articles/libxml-sax/libxml-sax.html
**
** 1. Walk Elements
** 2. XPath & Node Set 
** 3. Write Element
** 4. Write Attributes
**
** Attribute & Value
** xmlProp
** xmlContent
**
*/
#include "ak_types.h"
#include "ak_queue.h"
#include "ak_xml2.h"
#include <libxml/tree.h>
#include <libxml/xmlreader.h>
#include <libxml/xpath.h>

static 	int 	_gTraceLvl_Xml2 = TRACELVL_DEBUG;


/* ---------------------------------------------------------------------------
** We'll Add the XPath :
** 1. Name	-> Value.
** 2. Attribute -> Value.
*/
static 	int 	_SXml2_XPathExec(xmlXPathContext * pXPathCtx,  const char * hPath)
{
	int i, nr;
	xmlXPathObject 	* pXPathObj;
	xmlNodeSet 		* pNodeSet;
	xmlNode 		* pNode;
	xmlChar 		* pContent;
	
	if(pXPathCtx){
		/* Evalute the XPath */
		pXPathObj = xmlXPathEval((xmlChar *)hPath, pXPathCtx);
		
		/* Process the Node Set */
		pNodeSet = pXPathObj->nodesetval;
		if(!xmlXPathNodeSetIsEmpty(pNodeSet)){
			nr = pNodeSet->nodeNr;
			for(i=0; i<nr; i++){
				pNode = pNodeSet->nodeTab[i];
				if(pNode->type == XML_ELEMENT_NODE){
					pContent = xmlNodeGetContent(pNode);
					SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Name: %s \n", pNode->name);
					if(pContent){
						SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Value: %s \n", pContent);
					}
					
				}
			}
		}
		
		/* Clean */
		xmlXPathFreeObject(pXPathObj);
		return 0;
	}

	return -1;
}

static 	int 	_SXml2_XPath(xmlDoc * pDoc, xmlNode * pNode, const char * hPath) 
{
	int 	ret;
	xmlXPathContext * pXPathCtx; 

	ret = -1;
	/*
	pDoc    = xmlParseFile(hFile);
       pNode   = xmlDocGetRootElement(pDoc);
       */
	/* Init the Context */
	pXPathCtx = xmlXPathNewContext(pDoc);
	pXPathCtx->node = pNode;

	_SXml2_XPathExec(pXPathCtx, hPath);
	#if 0
	_SXml2_XPathExec(pXPathCtx, "//SCTP/RTOMax");
	_SXml2_XPathExec(pXPathCtx, "//SCTP/ValCookieLife[@type]");
	#endif

	/* Clean */
	xmlXPathFreeContext(pXPathCtx);

	return ret;
}



/* ---------------------------------------------------------------------------
** We'll Add the XmlReader :
*/
static 	int 	_SXml2Reader_NodeType(xmlTextReader * pReader, int nodeType, int depth)
{
	int ret = 0;
	switch(nodeType){
		case XML_READER_TYPE_ELEMENT:
			SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Element Beg: %d\n", depth);
			break;

		case XML_READER_TYPE_ATTRIBUTE:
			SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "ATTRIBUTE:\n");
			break;

		case XML_READER_TYPE_TEXT:
			SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "TEXT:\n");
			break;

		case XML_READER_TYPE_CDATA:
			SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "CDATA:\n");
			break;

		case XML_READER_TYPE_END_ELEMENT:
			SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Element End: %d\n", depth);
			break;
			
		default:
			#if 0
			SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "NodeType: %d\n", nodeType);
			#endif
			ret = 0;
			break;
	}
	return ret;
}

static 	int 	_SXml2Reader_Name(xmlTextReader * pReader, xmlChar * name,  xmlChar * localName, xmlChar * prefix)
{

	if(localName){
		SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "LocalName: %s\n", localName);
		xmlFree(localName);
	}
	#if 0
	if(prefix){
		SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Prefix : %s\n", prefix);
		xmlFree(prefix);
	}
	if(name){
		SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Name : %s\n", name);
		xmlFree(name);
	}
	#endif
	
	return 0;
}

static 	int 	_SXml2Reader_Value(xmlTextReader * pReader, xmlChar * value)
{
	if(value){
		SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Value: %s\n", value);
		xmlFree(value);
	}
	return 0;
}

static 	int 	_SXml2Reader_Attribute(xmlTextReader * pReader, int attributeCount)
{
	int 		i;
	xmlChar * 	attribute;
	xmlChar * 	value;
	for(i=0; i<attributeCount; i++){
		attribute = xmlTextReaderGetAttributeNo(pReader, i);
		if(attribute){
			value = xmlTextReaderGetAttribute(pReader, attribute);
			SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "[%-4d] Attribute: %s Value: %s\n", 
				i, attribute, value);
			xmlFree(attribute);
			xmlFree(value);
		}
		
	}
	return 0;
}

static 	void 	_SXml2Reader_Proc(xmlTextReader * pReader) {
/*
* NodeType: The node type, 1 for start element, 15 for end of element, 2 for attributes, 3 for text nodes, 4 for CData sections, 5 for entity references, 6 for entity declarations, 7 for PIs, 8 for comments, 9 for the document nodes, 10 for DTD/Doctype nodes, 11 for document fragment and 12 for notation nodes.
* Name: the qualified name of the node, equal to (Prefix:)LocalName.
* LocalName: the local name of the node.
* Prefix: a shorthand reference to the namespace associated with the node.
* NamespaceUri: the URI defining the namespace associated with the node.
* BaseUri: the base URI of the node. See the XML Base W3C specification.
* Depth: the depth of the node in the tree, starts at 0 for the root node.
* HasAttributes: whether the node has attributes.
* HasValue: whether the node can have a text value.
* Value: provides the text value of the node if present.
* IsDefault: whether an Attribute node was generated from the default value defined in the DTD or schema (unsupported yet).
* XmlLang: the xml:lang scope within which the node resides.
* IsEmptyElement: check if the current node is empty, this is a bit bizarre in the sense that <a/> will be considered empty while <a></a> will not.
* AttributeCount: provides the number of attributes of the current node.
*/
	int ret;
	int nodeType, depth;
	int hasAttributes, hasValue, attributeCount, isEmptyElement;
	xmlChar * name;
	xmlChar * localName;
	xmlChar * prefix;
	xmlChar * value;
	#if 0
	xmlChar * baseUri;
	xmlChar * nameSpaceUri;
	#endif
	
	/* Node Type & Depth */
	nodeType = xmlTextReaderNodeType(pReader);
	depth 	 = xmlTextReaderDepth(pReader);

	ret = _SXml2Reader_NodeType(pReader, nodeType, depth);
	if(ret == 0){
	
		/* Name & Local Name & Prefix & Uri */
		name 	 = xmlTextReaderName(pReader);
		localName= xmlTextReaderLocalName(pReader);
		prefix 	 = xmlTextReaderPrefix(pReader);
		ret = _SXml2Reader_Name(pReader, name, localName, prefix);

		#if 0
		baseUri 	= xmlTextReaderBaseUri(pReader);
		nameSpaceUri= xmlTextReaderNamespaceUri(pReader);
		#endif
		
		/* Attributes & Value */
		isEmptyElement	= xmlTextReaderIsEmptyElement(pReader);
		
		attributeCount	= 0;
		hasAttributes	= xmlTextReaderHasAttributes(pReader);
		if(hasAttributes == 1){
			attributeCount	= xmlTextReaderAttributeCount(pReader);
			_SXml2Reader_Attribute(pReader, attributeCount);
		}
		hasValue		= xmlTextReaderHasValue(pReader);
		if(hasValue == 1){
			value = xmlTextReaderValue(pReader);
			_SXml2Reader_Value(pReader, value);
		}
	}
	
}


/* ---------------------------------------------------------------------------
** We'll Add the SubTree & Next  :
*/
static 	int 	_SXml2Reader_SubTree(xmlTextReader * pReader, const char * hPath) {
	int 		ret;
	xmlDoc 	* 	pDoc;
	xmlNode * 	pSubTree;

	pSubTree = xmlTextReaderExpand(pReader);
	if(pSubTree){
		/* Add Process Here */
		pDoc= pSubTree->doc;
		_SXml2_XPath(pDoc, pSubTree, hPath);
	}
	ret = xmlTextReaderNext(pReader);
	return ret;
}



/* ---------------------------------------------------------------------------
** Xml2 Reader Parse :
*/
#define _SXML_STACK_PUSH(pStack, pFrame) \
	do{\
		if((pStack)->depth < (pStack)->total){\
			(pStack)->depth += 1;\
			pFrame = (pStack)->frame + (pStack)->depth;\
		}\
	}while(0)
	
#define _SXML_STACK_POP(pStack)	\
	do{\
		if((pStack)->depth > 0){	\
			(pStack)->depth -= 1;	\
		}\
	}while(0)

#define _SXML_STACK_CURR_GET(pStack, pFrame)	\
	do{\
		(pFrame) = NULL;\
		if( ((pStack)->depth >= 0) && \
			((pStack)->depth < (pStack)->total)){\
			pFrame = (pStack)->frame + (pStack)->depth;\
		}\
	}while(0)


static 	void 	_SXml2Reader_Parse_Element(xmlTextReader * pReader, _t_xml_stack_frame * pFrame){
	int i, j;

	xmlChar 	**	ppName;
	xmlChar 	* 	value;

	void 		* 	pCookie;
	void 		* 	pCookieA;
	_t_xml_child* 	pChild;
	_t_xml_desc * 	pParent;
	_t_xml_desc **	ppDesc;
	_t_xml_desc *	pDesc;
	_t_xml_attr * 	pAttr;

	value= xmlTextReaderLocalName(pReader);
	/* Check the Node */
	if(value && (pFrame) && (pParent = pFrame->parent)){
		pChild = &(pParent->child);
		ppDesc = pChild->childS;

		for(i=0; i<pChild->childC; i++, ppDesc++){
			/* Check the Child */
			if(	(ppDesc) && (pDesc = *ppDesc) && 
				(SYS_STRCMP((char *)value, (char *)(pDesc->name)) == 0)){
				#if 1
				SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Child(%d): %s\n", 
					i, value);
				#endif
				_SXML_FRAME_SET_PARENT(pFrame + 1, pDesc);

				/* Set Child */
				_SXML_FRAME_GET_COOKIE(pFrame, pCookie);
				if(pParent->cbSetChild){
					pCookie = pParent->cbSetChild(pCookie, pDesc->name, i);
					_SXML_FRAME_SET_COOKIE(pFrame + 1, pCookie);
				}

				/* Check the Attr */
				if(xmlTextReaderHasAttributes(pReader) == 1){
					pAttr = &(pDesc->attr);
					if(pAttr->attrC > 0){
						ppName = (xmlChar **)(pAttr->attrS);
						for(j=0; j<pAttr->attrC; j++, ppName++){
							if(*ppName){
								value = xmlTextReaderGetAttribute(pReader, *ppName);
								#if 1
								SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "Attr(%d) %s: %s\n", 
									j, *ppName, value);
								#endif
								if(value){
									/* Set Attr */
									if(pDesc->cbSetAttr){
										/* Dirty Hack !! ?? May Change the Cookie ?? */
										pCookieA = pDesc->cbSetAttr(pCookie, *ppName, j, value);
										if(pCookieA != pCookie){
											pCookie = pCookieA;
											_SXML_FRAME_SET_COOKIE(pFrame, 		pCookieA);
											_SXML_FRAME_SET_COOKIE(pFrame+1, 	pCookieA);
										}
									}
								}
							}
						}
					}
				}
				break;
			}
		}
	}

	return;
}

static 	void 	_SXml2Reader_Parse(xmlTextReader * pReader, _t_xml_stack * pStack) {
	int 				nodeType, depth;
	_t_xml_stack_frame * pFrame;
	
	/* Node Type & Depth */
	nodeType = xmlTextReaderNodeType(pReader);
	depth 	 = xmlTextReaderDepth(pReader);

	switch(nodeType){
		case XML_READER_TYPE_ELEMENT:
			#if 1
			SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_DEBUG, "\nDepth: XML:%d - Stack:%d\n", depth, pStack->depth);
			#endif
			/* Tricks : POP - PUSH - POP */
			if(depth == pStack->depth){
				_SXML_STACK_POP(pStack);
			}
			if(depth > pStack->depth){
				_SXML_STACK_PUSH(pStack, pFrame);
			}
			else if(depth < pStack->depth){
				do{
					_SXML_STACK_POP(pStack);
				}while(depth < pStack->depth);
			}

			_SXML_STACK_CURR_GET(pStack, pFrame);
			_SXml2Reader_Parse_Element(pReader, pFrame);
			break;
			
		default:
			break;
	}


	return;
}


int 	SXml2Reader_Parse(const char * hFile, _t_xml_stack * pStack) 
{
    int 			ret = -1;
    xmlTextReader 	* pReader;

    pReader = xmlNewTextReaderFilename(hFile);
    if (pReader != NULL) {
		
        ret = xmlTextReaderRead(pReader);
        while (ret == 1) {
            _SXml2Reader_Parse(pReader, pStack);
			#if 0
			_SXml2Reader_SubTree(pReader, hPath);
			#endif
            ret = xmlTextReaderRead(pReader);
        }

		
        xmlFreeTextReader(pReader);
        if (ret != 0) {
            SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_WARNING, "%s : Parse Fail\n", hFile);
        }
    } 
	else {
		SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_ERROR, "%s : Open Fail\n", hFile);
    }

	return ret;
}



int 	SXml2Reader_XPath(const char * hFile, const char * hPath) 
{
    int 			ret = -1;
    xmlTextReader 	* pReader;

    pReader = xmlNewTextReaderFilename(hFile);
    if (pReader != NULL) {
		
        ret = xmlTextReaderRead(pReader);
        while (ret == 1) {
            _SXml2Reader_Proc(pReader);
			#if 1
			_SXml2Reader_SubTree(pReader, hPath);
			#endif
            ret = xmlTextReaderRead(pReader);
        }

		
        xmlFreeTextReader(pReader);
        if (ret != 0) {
            SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_WARNING, "%s : Parse Fail\n", hFile);
        }
    } 
	else {
		SYS_TRACE(_gTraceLvl_Xml2, TRACELVL_ERROR, "%s : Open Fail\n", hFile);
    }

	return ret;
}


